home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch17 / Disk.cls < prev    next >
Text File  |  1999-07-06  |  16KB  |  519 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "RayDisk"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = False
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. ' A disk in a plane.
  17.  
  18. Implements RayTraceable
  19.  
  20. Private Point1 As Point3D   ' Point on plane.
  21. Private Point2 As Point3D   ' Normal = P2 - P1.
  22. Private Radius As Single    ' Radius.
  23.  
  24. ' Wire frame variables.
  25. Private Const WIRE_POINTS = 30
  26. Private WireFrame(1 To WIRE_POINTS) As Point3D
  27.  
  28. ' Ambient light parameters.
  29. Private AmbientKr As Single
  30. Private AmbientKg As Single
  31. Private AmbientKb As Single
  32.  
  33. ' Diffuse light parameters.
  34. Private DiffuseKr As Single
  35. Private DiffuseKg As Single
  36. Private DiffuseKb As Single
  37.  
  38. ' Specular reflection parameters.
  39. Private SpecularN As Single
  40. Private SpecularK As Single
  41.  
  42. ' Reflected light parameters.
  43. Private ReflectedKr As Single
  44. Private ReflectedKg As Single
  45. Private ReflectedKb As Single
  46.  
  47. ' Refracted light parameters.
  48. Private TransN As Single
  49. Private n1 As Single   ' Index of refraction outside the object.
  50. Private n2 As Single   ' Index of refraction inside the object.
  51. Private TransmittedKr As Single
  52. Private TransmittedKg As Single
  53. Private TransmittedKb As Single
  54.  
  55. Private IsReflective As Boolean
  56. Private IsTransparent As Boolean
  57. Private DoneOnThisScanline As Boolean
  58.  
  59. ' We had a hit on this scanline.
  60. Private HadHit As Boolean
  61.  
  62. ' We have had a hit on a previous scanline.
  63. Private HadHitOnPreviousScanline As Boolean
  64.  
  65. ' We will not be visible on later scanlines.
  66. Private ForeverCulled As Boolean
  67. ' Return an appropriate color for this object.
  68. Private Function GetColor() As Long
  69. Dim R As Integer
  70. Dim G As Integer
  71. Dim B As Integer
  72.  
  73.     R = 255 * (DiffuseKr + AmbientKr): If R > 255 Then R = 255
  74.     G = 255 * (DiffuseKg + AmbientKg): If G > 255 Then G = 255
  75.     B = 255 * (DiffuseKb + AmbientKb): If B > 255 Then B = 255
  76.     GetColor = RGB(R, G, B)
  77. End Function
  78. ' Return the right shade for this polygon.
  79. Private Function GetShade(ByVal pgon As SimplePolygon) As Long
  80. Dim i As Integer
  81. Dim px As Single
  82. Dim py As Single
  83. Dim pz As Single
  84. Dim light_source As LightSource
  85. Dim total_r As Single
  86. Dim total_g As Single
  87. Dim total_b As Single
  88. Dim R1 As Integer
  89. Dim g1 As Integer
  90. Dim b1 As Integer
  91. Dim empty_objects As Collection
  92.  
  93.     With pgon
  94.         ' Find a central point on this polygon.
  95.         For i = 1 To .PointX.Count
  96.             px = px + .PointX(i)
  97.             py = py + .PointY(i)
  98.             pz = pz + .PointZ(i)
  99.         Next i
  100.         px = px / .PointX.Count
  101.         py = py / .PointX.Count
  102.         pz = pz / .PointX.Count
  103.  
  104.         ' Add up the light components.
  105.         Set empty_objects = New Collection
  106.         For Each light_source In LightSources
  107.             CalculateHitColorDSA _
  108.                 1, empty_objects, Nothing, _
  109.                 EyeX, EyeY, EyeZ, _
  110.                 px, py, pz, .Nx, .Ny, .Nz, _
  111.                 DiffuseKr, DiffuseKg, DiffuseKb, AmbientKr, AmbientKg, AmbientKb, _
  112.                 SpecularK, SpecularN, R1, g1, b1
  113.             total_r = total_r + R1
  114.             total_g = total_g + g1
  115.             total_b = total_b + b1
  116.         Next light_source
  117.     End With
  118.  
  119.     If total_r > 255 Then total_r = 255
  120.     If total_g > 255 Then total_g = 255
  121.     If total_b > 255 Then total_b = 255
  122.  
  123.     GetShade = RGB(total_r, total_g, total_b)
  124. End Function
  125.  
  126. ' Return the unit surface normal.
  127. Private Sub GetUnitNormal(ByRef Nx As Single, ByRef Ny As Single, ByRef Nz As Single)
  128. Dim n_len As Single
  129.  
  130.     Nx = Point2.Trans(1) - Point1.Trans(1)
  131.     Ny = Point2.Trans(2) - Point1.Trans(2)
  132.     Nz = Point2.Trans(3) - Point1.Trans(3)
  133.     n_len = Sqr(Nx * Nx + Ny * Ny + Nz * Nz)
  134.     Nx = Nx / n_len
  135.     Ny = Ny / n_len
  136.     Nz = Nz / n_len
  137. End Sub
  138.  
  139. ' Add non-backface polygons to this collection.
  140. Public Sub RayTraceable_GetPolygons(ByRef num_polygons As Integer, polygons() As SimplePolygon, ByVal shaded As Boolean)
  141. Dim i As Integer
  142. Dim pgon As SimplePolygon
  143.  
  144.     ' Make a polygon.
  145.     Set pgon = New SimplePolygon
  146.  
  147.     ' Add points to the polygon.
  148.     For i = 1 To WIRE_POINTS
  149.         With WireFrame(i)
  150.             pgon.AddPoint .Trans(1), .Trans(2), .Trans(3)
  151.         End With
  152.     Next i
  153.  
  154.     ' See if we are shaded.
  155.     If shaded Then
  156.         ' We are shaded. Get the right color.
  157.         pgon.ForeColor = GetShade(pgon)
  158.         pgon.FillColor = pgon.ForeColor
  159.     Else
  160.         ' We are not shaded. Use the normal colors.
  161.         pgon.ForeColor = vbBlack
  162.         pgon.FillColor = GetColor()
  163.     End If
  164.  
  165.     ' Add the polygon to the list.
  166.     num_polygons = num_polygons + 1
  167.     ReDim Preserve polygons(1 To num_polygons)
  168.     Set polygons(num_polygons) = pgon
  169. End Sub
  170. ' Draw a wireframe for this object.
  171. Public Sub RayTraceable_DrawWireFrame(ByVal pic As PictureBox)
  172. Dim i As Integer
  173.  
  174.     ' Use an appropriate color.
  175.     pic.ForeColor = GetColor()
  176.  
  177.     ' Draw the disk.
  178.     With WireFrame(WIRE_POINTS)
  179.         pic.CurrentX = .Trans(1)
  180.         pic.CurrentY = .Trans(2)
  181.     End With
  182.     For i = 1 To WIRE_POINTS
  183.         With WireFrame(i)
  184.             pic.Line -(.Trans(1), .Trans(2))
  185.         End With
  186.     Next i
  187. End Sub
  188.  
  189. ' Initialize the object using text parameters in
  190. ' a comma-delimited list.
  191. Public Sub SetParameters(ByVal txt As String)
  192.     On Error GoTo DiskParamError
  193.  
  194.     ' Read the parameters and initialize the object.
  195.     ' Geometry.
  196.     Radius = CSng(GetDelimitedToken(txt, ","))
  197.     Point1.Coord(1) = CSng(GetDelimitedToken(txt, ","))
  198.     Point1.Coord(2) = CSng(GetDelimitedToken(txt, ","))
  199.     Point1.Coord(3) = CSng(GetDelimitedToken(txt, ","))
  200.     Point1.Coord(4) = 1
  201.     Point2.Coord(1) = Point1.Coord(1) + CSng(GetDelimitedToken(txt, ","))
  202.     Point2.Coord(2) = Point1.Coord(2) + CSng(GetDelimitedToken(txt, ","))
  203.     Point2.Coord(3) = Point1.Coord(3) + CSng(GetDelimitedToken(txt, ","))
  204.     Point2.Coord(4) = 1
  205.  
  206.     ' Ambient light.
  207.     AmbientKr = CSng(GetDelimitedToken(txt, ","))
  208.     AmbientKg = CSng(GetDelimitedToken(txt, ","))
  209.     AmbientKb = CSng(GetDelimitedToken(txt, ","))
  210.  
  211.     ' Diffuse reflection.
  212.     DiffuseKr = CSng(GetDelimitedToken(txt, ","))
  213.     DiffuseKg = CSng(GetDelimitedToken(txt, ","))
  214.     DiffuseKb = CSng(GetDelimitedToken(txt, ","))
  215.  
  216.     ' Specular reflection.
  217.     SpecularN = CSng(GetDelimitedToken(txt, ","))
  218.     SpecularK = CSng(GetDelimitedToken(txt, ","))
  219.  
  220.     ' Reflected light.
  221.     ReflectedKr = CSng(GetDelimitedToken(txt, ","))
  222.     ReflectedKg = CSng(GetDelimitedToken(txt, ","))
  223.     ReflectedKb = CSng(GetDelimitedToken(txt, ","))
  224.     IsReflective = (ReflectedKr > 0) Or (ReflectedKg > 0) Or (ReflectedKb > 0)
  225.  
  226.     ' Transmitted light.
  227.     TransN = CSng(GetDelimitedToken(txt, ","))
  228.     n1 = CSng(GetDelimitedToken(txt, ","))
  229.     n2 = CSng(GetDelimitedToken(txt, ","))
  230.     TransmittedKr = CSng(GetDelimitedToken(txt, ","))
  231.     TransmittedKg = CSng(GetDelimitedToken(txt, ","))
  232.     TransmittedKb = CSng(GetDelimitedToken(txt, ","))
  233.     IsTransparent = (TransmittedKr > 0) Or (TransmittedKg > 0) Or (TransmittedKb > 0)
  234.  
  235.     ' Make a wire frame.
  236.     MakeWireFrame
  237.  
  238.     Exit Sub
  239.  
  240. DiskParamError:
  241.     MsgBox "Error initializing disk parameters."
  242. End Sub
  243.  
  244. ' Make a wire frame.
  245. Private Sub MakeWireFrame()
  246. Dim i As Integer
  247. Dim X As Single
  248. Dim Y As Single
  249. Dim Z As Single
  250. Dim v1x As Single
  251. Dim v1y As Single
  252. Dim v1z As Single
  253. Dim v2x As Single
  254. Dim v2y As Single
  255. Dim v2z As Single
  256. Dim theta As Single
  257. Dim dtheta As Single
  258. Dim v1_scale As Single
  259. Dim v2_scale As Single
  260.  
  261.     GetLineNormals _
  262.         Point2.Coord(1) - Point1.Coord(1), _
  263.         Point2.Coord(2) - Point1.Coord(2), _
  264.         Point2.Coord(3) - Point1.Coord(3), _
  265.         v1x, v1y, v1z, v2x, v2y, v2z
  266.  
  267.     ' Make a polygon around the edge of the disk.
  268.     theta = 0
  269.     dtheta = 2 * PI / WIRE_POINTS
  270.     For i = 1 To WIRE_POINTS
  271.         v1_scale = Sin(theta) * Radius
  272.         v2_scale = Cos(theta) * Radius
  273.         With WireFrame(i)
  274.             .Coord(1) = Point1.Coord(1) + v2_scale * v1x + v1_scale * v2x
  275.             .Coord(2) = Point1.Coord(2) + v2_scale * v1y + v1_scale * v2y
  276.             .Coord(3) = Point1.Coord(3) + v2_scale * v1z + v1_scale * v2z
  277.             .Coord(4) = 1#
  278.         End With
  279.         theta = theta + dtheta
  280.     Next i
  281. End Sub
  282. ' Apply a transformation matrix to the object.
  283. Public Sub RayTraceable_Apply(M() As Single)
  284. Dim i As Integer
  285.  
  286.     ' Transform the wire frame.
  287.     For i = 1 To WIRE_POINTS
  288.         m3Apply WireFrame(i).Coord, _
  289.              M, WireFrame(i).Trans
  290.     Next i
  291.  
  292.     ' Transform the plane's points.
  293.     m3Apply Point1.Coord, M, Point1.Trans
  294.     m3Apply Point2.Coord, M, Point2.Trans
  295. End Sub
  296. ' Apply a transformation matrix to the object.
  297. Public Sub RayTraceable_ApplyFull(M() As Single)
  298. Dim i As Integer
  299.  
  300.     ' Transform the wire frame.
  301.     For i = 1 To WIRE_POINTS
  302.         m3ApplyFull WireFrame(i).Coord, _
  303.                  M, WireFrame(i).Trans
  304.     Next i
  305.  
  306.     ' Transform the plane's points.
  307.     m3ApplyFull Point1.Coord, M, Point1.Trans
  308.     m3ApplyFull Point2.Coord, M, Point2.Trans
  309. End Sub
  310.  
  311. ' Draw the object with backfaces removed.
  312. ' Draw the whole wire frame for planes.
  313. Public Sub RayTraceable_DrawBackfacesRemoved(ByVal pic As PictureBox)
  314.     RayTraceable_DrawWireFrame pic
  315. End Sub
  316. ' Return the red, green, and blue components of
  317. ' the surface at the hit position.
  318. Public Sub RayTraceable_FindHitColor(ByVal depth As Integer, Objects As Collection, ByVal eye_x As Single, ByVal eye_y As Single, ByVal eye_z As Single, ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByRef R As Integer, ByRef G As Integer, ByRef B As Integer)
  319. Dim Nx As Single
  320. Dim Ny As Single
  321. Dim Nz As Single
  322. Dim Vx As Single
  323. Dim Vy As Single
  324. Dim Vz As Single
  325. Dim NdotV As Single
  326.  
  327.     ' Find the unit normal at this point.
  328.     GetUnitNormal Nx, Ny, Nz
  329.  
  330.     ' Make sure the normal points towards the
  331.     ' center of projection.
  332.     Vx = EyeX - px
  333.     Vy = EyeY - py
  334.     Vz = EyeZ - pz
  335.     NdotV = Nx * Vx + Ny * Vy + Nz * Vz
  336.     If NdotV < 0 Then
  337.         Nx = -Nx
  338.         Ny = -Ny
  339.         Nz = -Nz
  340.     End If
  341.  
  342.     ' Get the hit color.
  343.     CalculateHitColor depth, Objects, Me, _
  344.         eye_x, eye_y, eye_z, _
  345.         px, py, pz, _
  346.         Nx, Ny, Nz, _
  347.         DiffuseKr, DiffuseKg, DiffuseKb, _
  348.         AmbientKr, AmbientKg, AmbientKb, _
  349.         SpecularK, SpecularN, _
  350.         ReflectedKr, ReflectedKg, ReflectedKb, IsReflective, _
  351.         TransmittedKr, TransmittedKg, TransmittedKb, TransN, n1, n2, IsTransparent, _
  352.         R, G, B
  353. End Sub
  354. ' See if the scanline plane with the indicated
  355. ' point and normal intersects this object.
  356. '
  357. ' Just see if the plane passes within distance
  358. ' Radius of the center.
  359. Public Sub RayTraceable_CullScanline(ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByVal Nx As Single, ByVal Ny As Single, ByVal Nz As Single)
  360. Dim dx As Single
  361. Dim dy As Single
  362. Dim dz As Single
  363. Dim dist As Single
  364.  
  365.     ' See if we will ever be visible again.
  366.     If ForeverCulled Then
  367.         DoneOnThisScanline = True
  368.         Exit Sub
  369.     End If
  370.  
  371.     ' We have not yet had a hit on this scanline.
  372.     HadHit = False
  373.  
  374.     ' Find the distance from the center of the
  375.     ' disk to the scanline plane.
  376.  
  377.     ' Get the vector from our center to the point.
  378.     With Point1
  379.         dx = .Trans(1) - px
  380.         dy = .Trans(2) - py
  381.         dz = .Trans(3) - pz
  382.     End With
  383.  
  384.     ' Take the dot product of this and the normal.
  385.     ' If the resulting distance > Radius, cull.
  386.     DoneOnThisScanline = (Abs(dx * Nx + dy * Ny + dz * Nz) > Radius)
  387.  
  388.     ' See if we will be culled in the future.
  389.     If DoneOnThisScanline Then
  390.         ' We were not culled on a previous scanline
  391.         ' but we are now. We will be culled on
  392.         ' all later scanlines.
  393.         If HadHitOnPreviousScanline Then ForeverCulled = True
  394.     Else
  395.         ' We are not culled. Remember that.
  396.         HadHitOnPreviousScanline = True
  397.     End If
  398. End Sub
  399. ' Return the value T for the point of intersection
  400. ' between the vector from point (px, py, pz) in
  401. ' the direction <vx, vy, vz>.
  402. '
  403. ' direct_calculation is true if we are finding the
  404. ' intersection from a viewing position ray. It is
  405. ' false if we are finding an reflected intersection
  406. ' or a shadow feeler.
  407. Public Function RayTraceable_FindT(ByVal direct_calculation As Boolean, ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByVal Vx As Single, ByVal Vy As Single, ByVal Vz As Single) As Single
  408. Dim A As Single
  409. Dim B As Single
  410. Dim C As Single
  411. Dim D As Single
  412. Dim Nx As Single
  413. Dim Ny As Single
  414. Dim Nz As Single
  415. Dim denom As Single
  416. Dim t As Single
  417. Dim Cx As Single
  418. Dim Cy As Single
  419. Dim Cz As Single
  420. Dim dx As Single
  421. Dim dy As Single
  422. Dim dz As Single
  423. Dim X As Single
  424. Dim Y As Single
  425. Dim Z As Single
  426.  
  427.     ' See if we have been culled.
  428.     If direct_calculation And DoneOnThisScanline Then
  429.         RayTraceable_FindT = -1
  430.         Exit Function
  431.     End If
  432.  
  433.     ' Find the unit normal at this point.
  434.     GetUnitNormal Nx, Ny, Nz
  435.  
  436.     ' Compute the plane's parameters.
  437.     A = Nx
  438.     B = Ny
  439.     C = Nz
  440.     D = -(Nx * Point1.Trans(1) + _
  441.           Ny * Point1.Trans(2) + _
  442.           Nz * Point1.Trans(3))
  443.  
  444.     ' If the denominator = 0, the ray is parallel
  445.     ' to the plane so there's no intersection.
  446.     denom = A * Vx + B * Vy + C * Vz
  447.     If denom = 0 Then
  448.         RayTraceable_FindT = -1
  449.         Exit Function
  450.     End If
  451.  
  452.     ' Solve for t.
  453.     t = -(A * px + B * py + C * pz + D) / denom
  454.  
  455.     ' If there is no positive t value, there's no
  456.     ' intersection in this direction.
  457.     If t < 0.01 Then
  458.         RayTraceable_FindT = -1
  459.         Exit Function
  460.     End If
  461.  
  462.     ' Get the coordinates of the disk's center.
  463.     Cx = Point1.Trans(1)
  464.     Cy = Point1.Trans(2)
  465.     Cz = Point1.Trans(3)
  466.  
  467.     ' Get the point of intersection with the plane.
  468.     X = px + t * Vx
  469.     Y = py + t * Vy
  470.     Z = pz + t * Vz
  471.  
  472.     ' See if the point is within distance
  473.     ' Radius of the center.
  474.     dx = Cx - X
  475.     dy = Cy - Y
  476.     dz = Cz - Z
  477.     If dx * dx + dy * dy + dz * dz > Radius * Radius Then
  478.         ' We are not within distance Radius.
  479.         RayTraceable_FindT = -1
  480.         Exit Function
  481.     End If
  482.  
  483.     ' We had a hit.
  484.     If direct_calculation Then HadHit = True
  485.  
  486.     RayTraceable_FindT = t
  487. End Function
  488. ' Return the minimum and maximum distances from
  489. ' this point.
  490. ' Use the wireframe points.
  491. Private Sub RayTraceable_GetRminRmax(new_min As Single, new_max As Single, ByVal X As Single, ByVal Y As Single, ByVal Z As Single)
  492. Dim i As Integer
  493. Dim dx As Single
  494. Dim dy As Single
  495. Dim dz As Single
  496. Dim dist As Single
  497.  
  498.     new_min = 1E+30
  499.     new_max = -1E+30
  500.  
  501.     For i = 1 To WIRE_POINTS
  502.         With WireFrame(i)
  503.             dx = X - .Trans(1)
  504.             dy = Y - .Trans(2)
  505.             dz = Z - .Trans(3)
  506.         End With
  507.         dist = Sqr(dx * dx + dy * dy + dz * dz)
  508.         If new_min > dist Then new_min = dist
  509.         If new_max < dist Then new_max = dist
  510.     Next i
  511. End Sub
  512. ' Reset the ForeverCulled flag.
  513. Private Sub RayTraceable_ResetCulling()
  514.     ForeverCulled = False
  515.     HadHitOnPreviousScanline = False
  516. End Sub
  517.  
  518.  
  519.